#include "model/clases/Clase.h"
#include "gui/Dibujar/Defines.h"
#include "model/Connector.h"
#include <iostream>
#include <string.h>

Clase::Clase(const std::string & name) {
	this->nombre = name;
	initializeConnectors();
	connectorsRefreshed = false;
	m_classType = NORMAL;
	m_type = "";
}

Clase::Clase(const Clase & other) : ComponentClasses(other) {
	this->nombre = other.getName();
	initializeConnectors();
	connectorsRefreshed = other.connectorsRefreshed;
	m_classType = other.m_classType;
	m_type = other.m_type;
	std::vector<Atributo*> vec = other.m_atributos;
	std::vector<Atributo*>::iterator it;
	for(it = vec.begin();it != vec.end(); it++)
	{
		Atributo* at = new Atributo(**it);
		m_atributos.push_back(at);
	}
	std::vector<Metodo*> vecMet = other.m_metodos;
	std::vector<Metodo*>::iterator iter;
	for(iter = vecMet.begin(); iter != vecMet.end(); iter++)
	{
		Metodo* met = new Metodo(**iter);
		m_metodos.push_back(met);
	}
}

Clase& Clase::operator=(const Clase& otro) {
	ComponentClasses::operator=(otro);
	initializeConnectors();
	return *this;
}

void Clase::addAtributo(Atributo* atr) {

	this->m_atributos.push_back(atr);
	connectorsRefreshed = false;

}

void Clase::addMetodo(Metodo* met) {

	this->m_metodos.push_back(met);
	connectorsRefreshed = false;

}

std::vector<Atributo*> Clase::getAtributos() {
	return this->m_atributos;
}

std::vector<Metodo*> Clase::getMetodos() {
	return this->m_metodos;
}

void Clase::intercambiarAtributos(std::vector<Atributo*> nuevos){
    std::vector<Atributo*>::iterator it;

    for(it = m_atributos.begin();it != m_atributos.end();it++){
        delete (*it);
    }
    m_atributos.clear();

    m_atributos = nuevos;
}

void Clase::intercambiarMetodos(std::vector<Metodo*> nuevos){
    std::vector<Metodo*>::iterator it;

    for(it = m_metodos.begin();it != m_metodos.end();it++){
        delete (*it);
    }
    m_metodos.clear();

    m_metodos = nuevos;
}

void Clase::serialize(XmlWriter & writer_rep, XmlWriter & writer_comp) {

	writer_rep.addRootChild(NODE_CLASS, "");

	/* Serializar mi representacion grafica */
	ComponentClasses::serialize(writer_rep , writer_comp);

	/* Serializo mi modelo */
	writer_comp.addRootChild(NODE_CLASS,"");
	ComponentClasses::saveMyComp(writer_comp);

	Utils utils;
	writer_comp.addCurrentNodeProperty("classtype", utils.convertToString(getClassType()).c_str());


	/* Serializo todos mis atributos*/
	for(std::vector<Atributo*>::iterator it = m_atributos.begin();
			it != m_atributos.end(); ++it)
	{
		(*it)->serialize(writer_rep,writer_comp);
	}
	/* y todos mis metodos*/
	for(std::vector<Metodo*>::iterator it = m_metodos.begin();
				it != m_metodos.end(); ++it)
	{
		(*it)->serialize(writer_rep,writer_comp);
	}
}

void Clase::deserialize(XmlReader & reader) {
}

void Clase::deserialize(XmlReader & reader, XmlReader& readerModel) {

	ComponentClasses::deserialize(reader);
	std::string id = reader.getCurrentNodeProperty(TARGET_ID);

	/* buscar mi modelo */
	readerModel.setCurrentNode(readerModel.getRoot());
	readerModel.setCurrentNode(readerModel.getCurrentNodeChild());
	xmlNode* nodo = NULL;
	bool found = false;
	while (readerModel.getCurrentNode() && !found) {
		if (!strcmp(readerModel.getCurrentNodeName(), NODE_CLASS)) {
			if (!readerModel.getCurrentNodeProperty(TARGET_ID).compare(id.c_str())) {
				found = true;
				nodo = readerModel.getCurrentNode();
			}
			else {
				if (readerModel.hastNextNode()) {
					readerModel.setCurrentNode(readerModel.nextNode());
				}
				else {
					readerModel.setCurrentNode(NULL);
				}
			}
		}
	}

	/* deserializo mis atributos y metodos */
	if (found) {
		readerModel.setCurrentNode(nodo);

		/* Obtener el tipo de clase */
		std::string classType = readerModel.getCurrentNodeProperty("classtype");
		Utils utils;
		int classTypeInt = utils.convertToInt(classType);
		switch (classTypeInt) {
			case NORMAL: {
				this->setClassType(NORMAL);break;
			}
			case ABSTRACT: {
				this->setClassType(ABSTRACT); break;
			}
			case INTERFACE: {
				this->setClassType(INTERFACE); break;
			}
			default:
				break;
		}

		/* Obtener metodos y atributos */
		readerModel.setCurrentNode(readerModel.getCurrentNodeChild());
		while (readerModel.getCurrentNode()) {
			if (!strcmp(readerModel.getCurrentNodeName(), NODE_METODO)) {
				Metodo* met = new Metodo();
				met->deserialize(reader, readerModel);
				this->addMetodo(met);
			}
			if (!strcmp(readerModel.getCurrentNodeName(), NODE_ATTRIBUTE)) {
					Atributo* atr = new Atributo();
					atr->deserialize(reader, readerModel);
					this->addAtributo(atr);
			}
			if (readerModel.hastNextNode()) {
				readerModel.setCurrentNode(readerModel.nextNode());
			}
			else {
				readerModel.setCurrentNode(NULL);
			}
		}
	}
}

void Clase::update() {
}

void Clase::validate(std::fstream & filestr, bool &valido) {

}

std::string Clase::description() {
	std::string d = "";
	d += "-------------------------------- \n";
	d += "CLASE: " + this->getName() + "\n";
	d += "-------------------------------- \n";
	for (unsigned int i = 0; i < getAtributos().size(); i++) {
		Atributo *a = getAtributos()[i];
		d += "  " + a->description() + ",\n";
	}
	d += "-------------------------------- \n";
	for (unsigned int i = 0; i < getMetodos().size(); i++) {
		Metodo *m = getMetodos()[i];
		d += "  " + m->description() + ",\n";
	}
	d += "-------------------------------- \n";
	return d;
}

void Clase::refreshConnectors(int widthBox, int heightBox) {
	if(!connectorsRefreshed){
		int relX = widthBox / (CLASS_CONNECTORS/4);
		int relY = heightBox / (CLASS_CONNECTORS/4);
		int j = 0 , value = 0;
		for (float i = 0; j < 20; i += relX, j++) {
			this->conectoresEntrantes[j]->setXRel(i);
			this->conectoresEntrantes[j]->setYRel(value);
			this->conectoresEntrantes[j]->setOrientation(NORTH);
			value = !value;
		}
		for (float i = 0; j < 40; i += relX, j++) {
			this->conectoresEntrantes[j]->setXRel(i);
			this->conectoresEntrantes[j]->setYRel(heightBox - value);
			this->conectoresEntrantes[j]->setOrientation(SOUTH);
			value = !value;
		}
		for (float i = 0; j < 60; i += relY, j++) {
			this->conectoresEntrantes[j]->setXRel(value);
			this->conectoresEntrantes[j]->setYRel(i);
			this->conectoresEntrantes[j]->setOrientation(WEST);
			value = !value;
		}
		for (float i = 0; j < 80; i += relY, j++) {
			this->conectoresEntrantes[j]->setXRel(widthBox - value);
			this->conectoresEntrantes[j]->setYRel(i);
			this->conectoresEntrantes[j]->setOrientation(EAST);
			value = !value;
		}

		connectorsRefreshed = true;
	}
}

void Clase::initializeConnectors() {
	componentType = ComponentTypeClasesClase;
	for (float j = 0; j < CLASS_CONNECTORS; j++) {
		addConnector(0, 0, true);
	}

	for (unsigned int i = 0; i < conectoresEntrantes.size(); i++) {
		conectoresEntrantes[i]->setIdParaMapeoDeConectores(i + 1);
	}

	// mapeo a conectores de entidad
	// west, del 1 al 5 en DER, del 40 al 60 en clases
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>( 1, 40));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>( 2, 45));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>( 3, 50));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>( 4, 55));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>( 5, 59));

	// north, del 6 al 15 en DER, del 0 al 20 en clases
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>( 6, 0));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>( 7, 1));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>( 8, 3));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>( 9, 5));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>(10, 9));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>(11, 11));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>(12, 14));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>(13, 16));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>(14, 18));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>(15, 19));

	// south, del 16 al 24 en DER, del 20 al 40 en clases
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>(16, 20));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>(17, 22));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>(18, 25));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>(19, 27));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>(20, 30));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>(21, 32));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>(22, 35));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>(23, 37));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>(24, 39));

	// east, del 25 al 28 en der, del 60 al 80 en clases
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>(25, 60));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>(26, 65));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>(27, 70));
	m_mapeoDeIdDeConectorEntidadAConectorClases.insert(std::pair<int, int>(28, 75));


	// Relacion
	m_mapeoDeIdDeConectorRelacionAConectorClases.insert(std::pair<int, int>(25, 60));
	m_mapeoDeIdDeConectorRelacionAConectorClases.insert(std::pair<int, int>(25, 60));
	m_mapeoDeIdDeConectorRelacionAConectorClases.insert(std::pair<int, int>(25, 60));
	m_mapeoDeIdDeConectorRelacionAConectorClases.insert(std::pair<int, int>(25, 60));
	m_mapeoDeIdDeConectorRelacionAConectorClases.insert(std::pair<int, int>(25, 60));

	m_mapeoDeIdDeConectorRelacionAConectorClases.insert(std::pair<int, int>(25, 60));
	m_mapeoDeIdDeConectorRelacionAConectorClases.insert(std::pair<int, int>(25, 60));
	m_mapeoDeIdDeConectorRelacionAConectorClases.insert(std::pair<int, int>(25, 60));
	m_mapeoDeIdDeConectorRelacionAConectorClases.insert(std::pair<int, int>(25, 60));
	m_mapeoDeIdDeConectorRelacionAConectorClases.insert(std::pair<int, int>(25, 60));

	m_mapeoDeIdDeConectorRelacionAConectorClases.insert(std::pair<int, int>(25, 60));
	m_mapeoDeIdDeConectorRelacionAConectorClases.insert(std::pair<int, int>(25, 60));
	m_mapeoDeIdDeConectorRelacionAConectorClases.insert(std::pair<int, int>(25, 60));
	m_mapeoDeIdDeConectorRelacionAConectorClases.insert(std::pair<int, int>(25, 60));
	m_mapeoDeIdDeConectorRelacionAConectorClases.insert(std::pair<int, int>(25, 60));

	m_mapeoDeIdDeConectorRelacionAConectorClases.insert(std::pair<int, int>(25, 60));
	m_mapeoDeIdDeConectorRelacionAConectorClases.insert(std::pair<int, int>(25, 60));
	m_mapeoDeIdDeConectorRelacionAConectorClases.insert(std::pair<int, int>(25, 60));
	m_mapeoDeIdDeConectorRelacionAConectorClases.insert(std::pair<int, int>(25, 60));

	refreshConnectors(ENTITY_HORIZONTAL_LINE, ENTITY_VERTICAL_LINE);
}

void Clase::setClassType(enum ClassType c){
	m_classType = c;
}

std::string Clase::getClassTypeString() const{
	std::string ret = "";
	if (m_classType == ABSTRACT) {
		ret = "\n<Abstract>";
	} else if (m_classType == INTERFACE) {
		ret = "\n<Interface>";
	}
	return ret;
}

enum ClassType Clase::getClassType() const{
	return m_classType;
}

int Clase::indiceDeConectorParaIdDeConectorDeEntidad(unsigned int id) {
	int i = m_mapeoDeIdDeConectorEntidadAConectorClases[id];
//	std::cout << "conector DER: " << id << " conector CLS: " << i << std::endl;
	return i;
}

int Clase::indiceDeConectorParaIdDeConectorDeRelacion(unsigned int id) {
	return m_mapeoDeIdDeConectorRelacionAConectorClases[id];
}
